// ActionScript file

package 
{
	import com.adobe.serialization.json.JSON;
	import com.adobe.serialization.json.JSONDecoder;
	
	import flare.animate.Transitioner;
	import flare.data.DataField;
	import flare.data.DataSchema;
	import flare.data.DataUtil;
	import flare.flex.FlareVis;
	import flare.vis.controls.DragControl;
	import flare.vis.controls.PanZoomControl;
	import flare.vis.data.Data;
	import flare.vis.data.DataSprite;
	import flare.vis.data.EdgeSprite;
	import flare.vis.operator.layout.RandomLayout;
	import flare.vis.util.Filters;
	
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Rectangle;
	import flash.net.*;
	import flash.utils.Dictionary;
	
	import mx.containers.Panel;
	import mx.controls.Alert;
	import mx.controls.CheckBox;
	import mx.controls.HSlider;
	import mx.controls.Label;
	import mx.controls.Menu;
	import mx.controls.PopUpButton;
	import mx.controls.RadioButton;
	import mx.controls.TextInput;
	import mx.core.Application;
	import mx.events.MenuEvent;
	import mx.events.SliderEvent;
	import mx.managers.CursorManager;
	
	public class Gnivz extends Application
	{
	
		private var nodeDictionary:Dictionary = new Dictionary();
		private var edgeDictionary:Dictionary = new Dictionary();
		
		private var menu:Menu;
		private var menuWidth:int = 190;

		public var search_field:TextInput;
		public var term_label:Label;
		private var search_term:String;
		
		public var labelsCheckBox:CheckBox;

		public var arrDPTags:Array;
		
		public var xmlDPTags:XML;
		public var xmlDPUsers:XML;
		public var xmlDPBkmarks:XML;			
		
		public var tagsPopup:PopUpButton;
		public var usersPopup:PopUpButton;
		public var bookmarksPopup:PopUpButton;
		
		public var tagRadioButton:RadioButton;
		public var userRadioButton:RadioButton;
		public var bookmarkRadioButton:RadioButton;
		
		public var tagUserDefinedRelationships:Boolean = true;
		public var tagCommonBookmarks:Boolean = true;
		public var tagCommonUsers:Boolean = true;
		
		public var userForTags:Boolean = true;
		public var userCommonBookmarks:Boolean = true;
		public var userCommonTags:Boolean = true;
		
		public var bookmarksCommonTags:Boolean = true;
		public var bookmarksRelatedTags:Boolean = true;
		
		public var instructions:Label;
		public var vizPanel:Panel;
		public var slider:HSlider;
		
		private var dataSchema:DataSchema;
		
		public var flareVis:FlareVis;
		
		private var urlLoader:URLLoader;
		
		/**
		 * initializes the visualization
		 */
		public function initVis():void
		{
			setupSchema();
			
			
			var WIDTH:Number = 595;
			var HEIGHT:Number = 590;
			
			var rectangle:Rectangle = new Rectangle(0,0,WIDTH, HEIGHT);
			
			flareVis.visualization.bounds = rectangle
			
			//layouts
			var layout:RandomLayout = new GnivzLayout();
			layout.layoutAnchor.x = 0;
			layout.layoutAnchor.y = 0;
			
			flareVis.visualization.operators.add(layout);
			flareVis.visualization.update();

			flareVis.visualization.controls.add(new PanZoomControl());
			var panZoomControl:PanZoomControl = flareVis.visualization.controls.getControlAt(0) as PanZoomControl;
			panZoomControl.hitArea = vizPanel;
            
            var dragControl:DragControl = new DragControl();
            dragControl.filter = Filters.isNodeSprite;
           
            flareVis.visualization.controls.add(dragControl);
            
            slider.addEventListener(SliderEvent.CHANGE, handleSlider);
            
            
		}
		
		public function initTagsPop():void 
		{
                menu = new Menu();
                menu.dataProvider = arrDPTags;
                menu.showRoot = false;
                menu.width = menuWidth;
                tagsPopup.popUp = menu;

	            menu.addEventListener(MenuEvent.CHANGE, function(event:MenuEvent):void {
		        	switch (event.item.label) {
		        		case "user defined relationships":
		        			if (menu.dataDescriptor.isToggled(event.item)) 
		        			{
		        				tagUserDefinedRelationships = true;
		        			} 
		        			else 
		        			{
		        				tagUserDefinedRelationships = false;
		        			}
		        			break;
		        		case "common bookmarks":
		        			if (menu.dataDescriptor.isToggled(event.item)) 
		        			{
		        				tagCommonBookmarks = true;
		        			}
		        			else 
		        			{
		        				tagCommonBookmarks = false;
		        			}
		        			break;
		        		case "common users":
		        			if (menu.dataDescriptor.isToggled(event.item)) 
		        			{
		        				tagCommonUsers = true;
		        			} 
		        			else 
		        			{
		        				tagCommonUsers = false;
		        			}
		        			break;
		        	}
	            });
        }
            
        public function initUsersPop():void 
        {
            menu = new Menu();
            menu.labelField = "@label";
            menu.dataProvider = xmlDPUsers;
            menu.showRoot = false;
            menu.width = menuWidth;
            usersPopup.popUp = menu;
            
            menu.addEventListener(MenuEvent.CHANGE, function(event:MenuEvent):void {
		        	switch (event.item.label) {
		        		case "for: tags":
		        			if (menu.dataDescriptor.isToggled(event.item)) 
		        			{
		        				userForTags = true;
		        			} 
		        			else 
		        			{
		        				userForTags = false;
		        			}
		        			break;
		        		case "common bookmarks":
		        			if (menu.dataDescriptor.isToggled(event.item)) 
		        			{
		        				userCommonBookmarks = true;
		        			} 
		        			else 
		        			{
		        				userCommonBookmarks = false;
		        			}
		        			break;
		        		case "common tags":
		        			if (menu.dataDescriptor.isToggled(event.item)) 
		        			{
		        				userCommonTags = true;
		        			} else {
		        				userCommonTags = false;
		        			}
		        			break;
		        	}
	            });
        }
        
        public function initBookmarksPop():void 
        {
            menu = new Menu();
            menu.labelField = "@label";
            menu.dataProvider = xmlDPBkmarks;
            menu.showRoot = false;
            menu.width = menuWidth;
            bookmarksPopup.popUp = menu;
            
            menu.addEventListener(MenuEvent.CHANGE, function(event:MenuEvent):void {
		        	switch (event.item.label) 
		        	{
		        		case "common tags":
		        			if (menu.dataDescriptor.isToggled(event.item)) 
		        			{
		        				bookmarksCommonTags = true;
		        			} 
		        			else 
		        			{
		        				bookmarksCommonTags = false;
		        			}
		        			break;
		        		case "related tags":
		        			if (menu.dataDescriptor.isToggled(event.item)) 
		        			{
		        				bookmarksRelatedTags = true;
		        			} 
		        			else 
		        			{
		        				bookmarksRelatedTags = false;
		        			}
		        			break;
		        	}
	            });
        }

		public function handleRadioTags(event:Event):void 
		{
	        // Handle event.
	        
	        tagsPopup.enabled = true;
	        usersPopup.enabled = false;
	        bookmarksPopup.enabled = false;
	    }
	
	    public function handleRadioUsers(event:Event):void 
	    {
	        // Handle event.
	        usersPopup.enabled = true;
	        tagsPopup.enabled = false;
	        bookmarksPopup.enabled = false;
	    }
	
		/**
	    * Event handler for selecting the bookamrks radio button
	    */	
	    public function handleRadioBookmarks(event:Event):void 
	    {
	        // Handle event.
	        
	        bookmarksPopup.enabled = true;
	        tagsPopup.enabled = false;
	        usersPopup.enabled = false;
	    }
	    
	    /**
	    * Event handler for Visualize button
	    */
	    public function visualize():void
	    {   	
	    	CursorManager.setBusyCursor();
	    	
   		   	updateSearchTerm();
	    	
	    	if(instructions.visible == true)
	    	{
	    		instructions.visible = false;
	    	}
			
			if( flareVis.visualization.data.size == 0)
			{
				initVis();
			}
			else
			{
				//why isn't this working??
				
				//start with an empty dataset
				flareVis.visualization.data.clear();
				
				nodeDictionary = null;
				edgeDictionary = null;
				
				nodeDictionary = new Dictionary();
				edgeDictionary = new Dictionary();
			}
			
			labelsCheckBox.selected = true;

			getJSON(search_term);  //upon Event.COMPLETE updateVisualization() is called

	    }
	    
	    /**
	    * updates the search term
	    */
	    private function updateSearchTerm():void
	    {
	    	
	    	search_term = search_field.text;
	    	term_label.text = search_term;
	    }
	    
	    /**
	    * 
	    */
	    private function getJSON(tag:String):void
	    {
	    	if(tagRadioButton.selected == true)
	    	{
	    		var jsonURL:String = "http://localhost:8080/gnizr/gnivz/getTags.action?tag="
	    		+ tag + "&userDefinedRelationsEnabled=" + tagUserDefinedRelationships
	    		+ "&commonBookmarksEnabled=" + tagCommonBookmarks
	    		+ "&commonUsersEnabled=" + tagCommonUsers;
	    	}
	    	else if(userRadioButton.selected == true)
	    	{
	    	}

	    	trace("url = " + jsonURL);
	    	urlLoader = new URLLoader(new URLRequest(jsonURL));
	    	urlLoader.addEventListener(Event.COMPLETE, updateVisualization);
	    }
	    
	    /**
	    * parsing by hand
	    */ 
	    private function parseJSON(data:Data, jsonString:String):void {
	    	var o:Object = (new JSONDecoder(jsonString)).getObject();
	    	for each (var node in o.node_array) 
	    	{
	    		var nodeSprite:GnivzNodeSprite = new GnivzNodeSprite(node.name, node.url, "");
	    		
	    		nodeSprite.doubleClickEnabled = true;
	    		nodeSprite.addEventListener(MouseEvent.DOUBLE_CLICK, handleNodeDoubleClick);
				nodeSprite.addEventListener(MouseEvent.CLICK, handleNodeClick);
				nodeSprite.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOver);
				nodeSprite.addEventListener(MouseEvent.MOUSE_OUT, handleMouseOver);
				nodeSprite.buttonMode = true;
	    		
	    		if (!nodeDictionary[node.name]) 
	    		{
		    		//trace("ADDING node.name: " + node.name + " node.url: " + node.url);
	    			data.addNode(nodeSprite);
	    			nodeDictionary[node.name] = nodeSprite;
	    		}
	    	}
	    	for each (var edge in o.edge_array) 
	    	{
	    		if (edge.weight == 0) 
	    		{
	    			continue;
	    		}
	    		if (!edgeDictionary[edge.to + "_" + edge.from] && !edgeDictionary[edge.from + "_" + edge.to]) 
	    		{
		    		
		    		//trace("ADDING edge.to: " + edge.to + " edge.from: " + edge.from + " edge.weight: " + edge.weight);
	    			
	    			var edgeSprite:EdgeSprite = data.addEdgeFor(nodeDictionary[edge.from], nodeDictionary[edge.to], false, edge.weight);
					
					if(edgeSprite.data < 33)
					{
						edgeSprite.lineWidth = 1;
					}
					else if(edgeSprite.data < 66)
					{
						edgeSprite.lineWidth = 2;
					}
					else
					{
						edgeSprite.lineWidth = 3;
					}
					
					edgeSprite.addEventListener(MouseEvent.CLICK, handleEdgeClick);
					edgeSprite.buttonMode = true;
	    			edgeDictionary[edge.to + "_" + edge.from] = edgeSprite;
	    		}
	    	}
	    }
	    
	    /**
	    * 
	    */
	    private function setupSchema():void
	    {
	    	var nodeField:DataField;
	    	var edgeField:DataField;
	    	var hoverField:DataField;
	    	
	    	dataSchema = new DataSchema();
	    	
	    	nodeField = new DataField("node_array", DataUtil.STRING);
	    	edgeField = new DataField( "edge_array", DataUtil.STRING);
	    	
	    	dataSchema.addField(nodeField);
	    	dataSchema.addField(edgeField);
	    }
	    
	    /**
	    * 
	    */ 
	    private function updateVisualization(event:Event):void
	    {	
			parseJSON(flareVis.visualization.data, urlLoader.data);			
			flareVis.visualization.update(new Transitioner(2)).play(); 
			
			CursorManager.removeBusyCursor();
	    }
	    
	    
	    /**
	    * handles the slider
	    */
	    private function handleSlider(event:SliderEvent):void
	    {    	
	    	var sliderValue:Number = (event.target as HSlider).value;
	    	
	    	setColors(sliderValue);
	    }
	    
	    /**
	    * called by edge's event listener on MouseEvent.CLICK
	    */
	    private function handleEdgeClick(event:MouseEvent):void
	    {
	    	//change color
	    	setColors(slider.value);
	    	
	    	//highlight the selected edge
	    	event.target.lineColor = 0xFF3399FF;
	    }
	    
	    /**
	    * 
	    */
	    private function handleMouseOver(event:MouseEvent):void
	    {
	    	if( event.target is GnivzNodeSprite)
	    	{
		    	if( (event.target as GnivzNodeSprite).ts.visible == false
		    		&& event.type == MouseEvent.MOUSE_OVER )
		    	{
		    		(event.target as GnivzNodeSprite).ts.visible = true;
		    	}
		    	
		    	//if the event is MOUSE_OUT and the node is faded, set the label to false
		    	if( (event as MouseEvent).type == MouseEvent.MOUSE_OUT 
		    	&& (event.target.fillColor == 0x33FF6600 || (labelsCheckBox.selected == false)) )
		    	{
		    		(event.target as GnivzNodeSprite).ts.visible = false;
		    	}
		    }
	    	
	    }
	    
	    /**
	    * handles double click to expand a node
	    */
	    private function handleNodeDoubleClick(event:MouseEvent):void
	    {
	    	//trace("handleNodeDoubleClick: " + event.target);
	    	
	    	CursorManager.setBusyCursor();
	    	
	    	getJSON((event.target as GnivzNodeSprite).getNodeName());
	    }
	    
	    /**
	    * called by node's event listener on MouseEvent.CLICK
	    */
	    private function handleNodeClick(event:MouseEvent):void
	    {  	
	    	
	    	if(event.ctrlKey == true)
	    	{
	    		//show in gnizr
	    		var link:URLRequest = new URLRequest( (event.target as GnivzNodeSprite).getURL() );
				navigateToURL(link, "_blank");
	    	}
	    	else
	    	{
	    		//change color
				setColors(slider.value);
	   
	    		//highlight the selected node
	    		if(event.target is GnivzNodeSprite)
	    		{
	    			event.target.fillColor = 0xFFFF6600;
	    			(event.target as GnivzNodeSprite).ts.visible = true;
	    		}
	    	}
	    }
	    
	    private function setColors(sliderValue:Number)
	    {
	    	flareVis.visualization.data.visit(function(edge:EdgeSprite):void
	    		{
	    			if(edge.data < sliderValue && edge.data != 0)
	    			{
	    				edge.lineColor = 0x33CCCCCC;
	    				
	    				(edge.source as GnivzNodeSprite).ts.visible = false;
	    				edge.source.fillColor = 0x33FF6600;
	    				edge.source.lineColor = 0x33FF6600;
	    				
	    				(edge.target as GnivzNodeSprite).ts.visible = false;
	    				edge.target.fillColor = 0x33FF6600;
	    				edge.target.lineColor = 0x33FF6600;
	    			}
	    			else
	    			{
	    				edge.lineColor = 0xFFCCCCCC;
	    				
	    				(edge.source as GnivzNodeSprite).ts.visible = true;
	    				edge.source.fillColor = 0x66FF6600;
	    				edge.source.lineColor = 0xFFFF6600;
	    				
	    				(edge.target as GnivzNodeSprite).ts.visible = true;
	    				edge.target.fillColor = 0x66FF6600;
	    				edge.target.lineColor = 0xFFFF6600;
	    			}
	    		}, Data.EDGES);
	    	
	    }
	    
	    /**
	    * turns the labels on/off
	    */
	   	public function toggleLabels():void
	    {
	    	flareVis.visualization.data.visit(function(node:GnivzNodeSprite):void 
	    	{
	    		node.ts.visible = !node.ts.visible;
			}, Data.NODES);
	    }

	}
}